package inge_soft.gestoriusNuevo.persistence.DAO.implementation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import inge_soft.gestoriusNuevo.persistence.DAO.Exceptions.Abstraction.GestoriusException;
import inge_soft.gestoriusNuevo.persistence.DAO.Exceptions.Implementation.ErrorMergeInstitucion;
import inge_soft.gestoriusNuevo.persistence.DAO.Exceptions.Implementation.ErrorMergeUsuario;
import inge_soft.gestoriusNuevo.persistence.DAO.Exceptions.Implementation.UsuarioInexistenteException;
import inge_soft.gestoriusNuevo.persistence.DAO.Exceptions.Implementation.UsuarioRegistradoMalFormadoException;
import inge_soft.gestoriusNuevo.persistence.DAO.abstraction.IUsuarioRegistradoDAO;
import inge_soft.gestoriusNuevo.persistence.entity.implementation.DomicilioTipificado;
import inge_soft.gestoriusNuevo.persistence.entity.implementation.EstadoInstitucion;
import inge_soft.gestoriusNuevo.persistence.entity.implementation.Institucion;
import inge_soft.gestoriusNuevo.persistence.entity.implementation.Usuario;
import inge_soft.gestoriusNuevo.persistence.entity.implementation.UsuarioRegistrado;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.zkoss.zul.Messagebox;

@Repository("usuarioRegistradoDAO")
public class UsuarioRegistradoDAO implements IUsuarioRegistradoDAO {
	@PersistenceContext
	private EntityManager entityManager;


	static final Logger logger = Logger.getLogger(UsuarioRegistradoDAO.class);

	
	public UsuarioRegistradoDAO(){
		
	}
	
	/*
	 * Busca en la base de datos un Usuario Registrado realizando una query por nick
	 * 
	 * 
	 */
	@Transactional(readOnly = true)
	public UsuarioRegistrado findByNick(String nick) throws UsuarioInexistenteException{

		try {
			System.out.println("llega con: " + nick);
			this.existeUsuario(new UsuarioRegistrado(nick, null));
		} catch (UsuarioInexistenteException e1) {
			throw new UsuarioInexistenteException("Ingrese un nick valido");
		}

		Query qUsrReg = entityManager.createQuery(
				"SELECT u FROM UsuarioRegistrado u where u.nick = :usrNick")
				.setParameter("usrNick", nick);
		UsuarioRegistrado userReg = (UsuarioRegistrado) qUsrReg
				.getSingleResult();
		if (userReg == null)
			throw new UsuarioInexistenteException("Ingrese un nick valido");

		return userReg;

	}
	
	
	@Transactional(readOnly = true)
	public UsuarioRegistrado findByUsuario(Usuario usuario)
			throws UsuarioInexistenteException {

		Query qUsrReg = entityManager.createQuery(
				"SELECT u FROM UsuarioRegistrado u where u.usr = :user")
				.setParameter("user", usuario);
		UsuarioRegistrado userReg = (UsuarioRegistrado) qUsrReg
				.getSingleResult();
		if (userReg == null)
			throw new UsuarioInexistenteException("Usuario invalido");

		return userReg;

	}
	
	
	/*
	 * Crea un nuevo usuario con los parametros que le llegan
	 * 
	 */
	
	@Transactional(propagation = Propagation.MANDATORY)
	public void crearUsuario(UsuarioRegistrado usr) throws DataAccessException{

		entityManager.persist(usr);
		
		if (!logger.isDebugEnabled()) {
			logger.info("Persistido " + usr.toString());
		}
		
		
	}
	
	/*
	 * Actualiza los datos del usuario recibido como parametro
	 * 
	 */
	
	@Transactional(propagation = Propagation.MANDATORY)
	public void modificarUsuario(UsuarioRegistrado usr) throws DataAccessException {
		try{
			entityManager.merge(usr);
		}
		catch(Exception e){
			System.out.println("Edxcepcion original :"+e.getMessage());
			throw new ErrorMergeUsuario("Error al actualizar los datos del usuario");
		}
		if (!logger.isDebugEnabled()) {
			logger.info("Actualizado " + usr.toString());
		}
	}
	
	/*
	 * Elimina los datos de un usuario. ToDo!!!
	 * 
	 */
	
	
	@Transactional(propagation = Propagation.MANDATORY)
	public void eliminarUsuario(UsuarioRegistrado usr) throws DataAccessException{
		// TODO Auto-generated method stub
		
	}
	
	/*
	 * Verifica si existe el usuario mediante una query con un count * 
	 * En caso de que exista al menos uno, lanza excepcion "Usuario inexistente"
	 * 
	 */
	@Transactional(readOnly = true)
	public boolean existeUsuario(UsuarioRegistrado usr) throws GestoriusException {
		long result;
		Query q = entityManager.createQuery("SELECT count(u.nick) FROM UsuarioRegistrado u where u.nick = :usrNick").setParameter("usrNick", usr.getNick() );
		result = ((Long)q.getSingleResult());
		if( result == 0L){
			throw new UsuarioInexistenteException("Verifique los datos ingresados");
		}
		else if(result > 1L){
			throw new UsuarioInexistenteException("Error inesperado");	
		}
		return true;
		
	}
	
	
	/*
	 * Verifica los datos ingresados para login.
	 * Utiliza el metodo existeUsuario(nick) para comprobar el usuario 
	 * Utiliza una query para obtener la contrase�a del usuario si este existe 
	 * Si existe un usuario con ese nick y esa pass, retorna el usuario de la base de datos
	 */
	
	@Transactional(readOnly = true)
	public UsuarioRegistrado verificarLogin(UsuarioRegistrado usr) throws GestoriusException{
		try{
		this.existeUsuario(usr);
		}
		catch(UsuarioInexistenteException e1){
			//System.out.println("rethrow dao");
			    throw e1;
			
		}
		Query qPass = entityManager.createQuery("SELECT u.contrasenia FROM UsuarioRegistrado u where u.nick = :usrNick").setParameter("usrNick", usr.getNick() );
		
		if(((String)qPass.getSingleResult()).compareTo(usr.getContrasenia())!=0){
			throw new UsuarioInexistenteException("Verifique los datos ingresados");
		}
		
		
		return findByNick(usr.getNick());
	}




	public List<UsuarioRegistrado> listAllOrderByName()
			throws GestoriusException {
		Query qPass = entityManager.createQuery("SELECT u.contrasenia FROM UsuarioRegistrado u orderby u.nick");
		return qPass.getResultList();
	}	
	
	/////// Dao de instituciones
	
	
	// Obtiene las instituciones de un usuario
	public List<Institucion> obtenerInstituciones(String nickUsuario)
			throws DataAccessException {
		Collection<Institucion> instituciones;
		try {
			instituciones = (findByNick(nickUsuario))
					.getUsuario().getInstituciones();
		} catch (UsuarioInexistenteException e1) {
			throw e1;
		}
		return new ArrayList<Institucion>(instituciones);
	}
	
	
	// Agrega una institucion a la lista de instituciones de un usuario
	public void crearInstitucion(String nickUsuario,
			Institucion institucionNueva) throws DataAccessException {
		try {
			UsuarioRegistrado userR = findByNick(nickUsuario);
			institucionNueva.setUsuario(userR.getUsuario());
			EstadoInstitucion estadoNuevaInstitucion = buscarEstado("Activa");
			institucionNueva.setEstado(estadoNuevaInstitucion);
			institucionNueva.setDomicilios(new ArrayList<DomicilioTipificado>());
			entityManager.persist(institucionNueva);
			
			userR.getUsuario().getInstituciones().add(institucionNueva);
			modificarUsuario(userR);
		} catch (UsuarioInexistenteException e1) {
			throw e1;
		} catch (DataAccessException e2) {
			throw e2;
		}
		
	}
	
	//Modifica la institucion de un usuario
	public void modificarInstitucion(String nickUsuario,
			Institucion institucionModificada) throws DataAccessException {
		try {
			UsuarioRegistrado userR = findByNick(nickUsuario);
			List<Institucion> instituciones = new ArrayList<Institucion>(userR
					.getUsuario().getInstituciones());
			if (instituciones.contains(institucionModificada)) {
				int index = instituciones.indexOf(institucionModificada);
				instituciones.set(index, instituciones.get(index));
				modificarUsuario(userR);
			} else {
				throw new ErrorMergeInstitucion(
						"El usuario no posee esa institucion");
			}
		}
		catch (UsuarioInexistenteException e1) {
			throw e1;
		
		}
		catch(DataIntegrityViolationException e3){
			throw e3;
		}
		catch (DataAccessException e2) {
			throw e2;
		}		
	}
	
	public EstadoInstitucion buscarEstado(String estado){
		
		List<EstadoInstitucion> estadosPosibles =  obtenerEstadosInstitucion();
		EstadoInstitucion estadoElegido = null;
		for(EstadoInstitucion estadoActual:estadosPosibles){
			if(estadoActual.getEstado().equalsIgnoreCase(estado)){
				estadoElegido = estadoActual;
			}
		}
		
		return estadoElegido;
	}
	
	//Lista los estados posibles que puede tomar una institucion 
	public List<EstadoInstitucion> obtenerEstadosInstitucion(){
		return entityManager.createQuery("SELECT p FROM EstadoInstitucion p").getResultList();
	}
	
}
